import { StateContext } from '@ngxs/store';
import { elvis } from './util';
import { StoreBase } from './state.base';
import { Type } from '@angular/core';
import { EditState } from './contracts';

export interface EntityStateModel<T> {
  currentModel: T;
  editState: EditState;
}

export function defaultEntityState(): EntityStateModel<any> {
  return {
    currentModel: {},
    editState: EditState.Empty,
  };
}

export type ExtendsEntityStore<T extends { id: string }> = Type<EntityStore<T>>;

export abstract class EntityStore<T extends { id: string }> extends StoreBase<EntityStore<T>> {
  public static get getEditState() {
    return state => {
      return elvis(state, this.staticStorePath).editState;
    };
  }

  public static get getCurrentModel() {
    return state => {
      return elvis(state, this.staticStorePath).currentModel;
    };
  }

  constructor(storeClass: ExtendsEntityStore<T>) {
    super(storeClass);
    this.setup(
      storeClass,
      'changeToEmpty',
      'changeToCreating',
      'changeToView',
      'changeToViewNewModel',
      'changeToEditing',
      'save',
      'edit',
      'delete'
    );
  }

  public changeToEmpty({ patchState }: StateContext<EntityStateModel<T>>) {
    patchState({
      currentModel: {} as T,
      editState: EditState.Empty,
    });
  }

  public changeToCreating({ patchState }: StateContext<EntityStateModel<T>>) {
    patchState({
      currentModel: {} as T,
      editState: EditState.Creating,
    });
  }

  public changeToView({ getState, patchState }: StateContext<EntityStateModel<T>>) {
    var { currentModel } = getState();
    patchState({
      currentModel: { ...currentModel },
      editState: EditState.View,
    });
  }

  public changeToViewNewModel({ patchState }: StateContext<EntityStateModel<T>>, { payload }: any) {
    return this.onGet(payload).then(model => {
      patchState({
        currentModel: model,
        editState: EditState.View,
      });
    });
  }

  public changeToEditing({ patchState }: StateContext<EntityStateModel<T>>) {
    patchState({
      editState: EditState.Editing,
    });
  }

  public save({ patchState }: StateContext<EntityStateModel<T>>, { payload }: any) {
    return this.onSave(payload).then(model => {
      patchState({
        currentModel: model,
        editState: EditState.View,
      });
    });
  }

  public edit({ patchState }: StateContext<EntityStateModel<T>>, { payload }: any) {
    return this.onUpdate(payload).then(model => {
      patchState({
        currentModel: model,
        editState: EditState.View,
      });
    });
  }

  public delete({ patchState }: StateContext<EntityStateModel<T>>, { payload }: any) {
    return this.onDelete(payload).then(model => {
      patchState({
        currentModel: {} as T,
        editState: EditState.Empty,
      });
    });
  }

  protected abstract onGet(payload: any): Promise<T>;
  protected abstract onSave(payload: any): Promise<T>;
  protected abstract onUpdate(payload: any): Promise<T>;
  protected abstract onDelete(payload: any): Promise<any>;
}
